#include <string>
#include <memory>
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>

enum
{
    Con_error = 1
};

const size_t msg_size = 4096;
const size_t reconnect_num = 5;

class tcp_client
{
public:
    tcp_client(const std::string &ip, const uint16_t port)
        : _server_ip(ip), _server_port(port), _sockfd(-1)
    {
        _server.sin_family = AF_INET;
        inet_pton(AF_INET, _server_ip.c_str(), &(_server.sin_addr));
        _server.sin_port = htons(_server_port);
    }

    // void init()
    // {
    //     _sockfd = socket(AF_INET, SOCK_STREAM, 0);
    //     // 与udp相同，客户端不需要显示的bind
    //     socklen_t len = sizeof _server;
    //     if (connect(_sockfd, (const sockaddr *)&_server, len))
    //     {
    //         std::cerr << "connet to server error, " << strerror(errno) << std::endl;
    //         exit(Con_error);
    //     }
    // }

    void run()
    {
        char message[msg_size] = {0};
        auto close_fd = [](int *pfd)
        {
            close(*pfd);
            // std::cout << "close success!\n";
        };
        while (true)
        {
            if (new_connect())
            {
                std::cerr << "user offline!" << std::endl;
                exit(Con_error);
            }
            std::unique_ptr<int, decltype(close_fd)> pfd(&_sockfd, close_fd);
            // std::unique_ptr<int, close>()
            std::string s;
            // 1. 发送消息
            std::cout << "please enter: ";
            std::getline(std::cin, s);
            ssize_t n = write(_sockfd, s.c_str(), s.size());
            if (n < 0)
            {
                std::cerr << "write fail..." << std::endl;
                // close(_sockfd);
                continue;
            }
            n = read(_sockfd, message, sizeof message - 1);
            // if (n == 0)
            // {
            //     std::cerr << "server close!\n";
            //     close(_sockfd);
            //     break;
            // }
            if (n <= 0)
            {
                std::cerr << "read error\n";
                // close(_sockfd);
                continue;
            }

            message[n] = 0;
            std::cout << message << std::endl;
            // close(_sockfd);
            _sockfd = -1;
        }
    }

    ~tcp_client()
    {
        if (_sockfd > 0)
            close(_sockfd);
    }

private:
    bool new_connect()
    {
        int cnt = reconnect_num;
        bool wait_reconnect = false;
        _sockfd = socket(AF_INET, SOCK_STREAM, 0);

        do
        {
            // 与udp相同，客户端不需要显示的bind
            socklen_t len = sizeof _server;
            if (connect(_sockfd, (const sockaddr *)&_server, len))
            {
                std::cerr << "connet to server error, try reconnect, " << (cnt--) << std::endl;
                errno = 0;
                wait_reconnect = true;
                sleep(1);
            }
            else
                wait_reconnect = false;
        } while (cnt && wait_reconnect);
        return wait_reconnect;
    }

private:
    int _sockfd;
    bool _is_running = false;
    std::string _server_ip;
    uint16_t _server_port;
    struct sockaddr_in _server;
};